/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.spring.mvc;

import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.internna.iwebjtracker.model.DefaultIssue;
import org.internna.iwebjtracker.model.DefaultVersion;
import org.internna.iwebmvc.dao.DAO;
import org.internna.iwebmvc.model.UUID;
import org.internna.iwebmvc.utils.Assert;
import org.internna.iwebmvc.crypto.Decipherer;
import org.internna.iwebmvc.utils.CollectionUtils;
import org.springframework.ui.ModelMap;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.web.bind.annotation.RequestMapping;

import static org.internna.iwebmvc.utils.StringUtils.hasText;

/**
 * Generates a changelog for a particular version or milestone.
 *
 * @author Jose Noheda
 * @since 1.0
 */
@Controller
@RequestMapping("/changelog.iwebmvc")
public final class ChangelogController {

    /**
     * Key (JSP) for the collection of version/issues
     */
    public static final String ISSUES = "ISSUES";

    /**
     * Key (JSP) for the version asked
     */
    public static final String VERSION = "VERSION";

    private DAO dao;
    private Decipherer decipherer;

    private final Set<String> collections;

    /**
     * Establishes the DefaultProject collections: owners, versions, modules, developers
     */
    public ChangelogController() {
        collections = new HashSet<String>();
        collections.add("milestones");
        collections.add("parentProject");
        collections.add("parentVersion");
    }

    /**
     * Spring injection point (REQUIRED)
     *
     * @param dao a not null persistence facade
     */
    @Required public void setDao(DAO dao) {
        Assert.notNull(dao);
        this.dao = dao;
    }

    /**
     * Spring injection point (REQUIRED)
     *
     * @param decipherer a not null implementation
     */
    @Required public void setDecipherer(Decipherer decipherer) {
        Assert.notNull(decipherer);
        this.decipherer = decipherer;
    }

    /**
     * Changes the objects and collections of the target entity that will be automatically
     * initialized in the view.
     *
     * @param collections any
     */
    public void setCollectionsToInitialize(Collection<String> collections) {
        this.collections.clear();
        if (CollectionUtils.isNotEmpty(collections)) {
            this.collections.addAll(collections);
        }
    }

    /**
     * Accessor method for testing purposes.
     */
    protected Set<String> getCollections() {
        return collections;
    }

    /**
     * Gets the version identifier and generates the changelog
     *
     * @param uuid provided by the "project" URL parameter
     * @param model injected by Spring MVC automatically
     *
     * @return /project/index
     */
    @RequestMapping(method = RequestMethod.GET)
    public String fillModel(@RequestParam(value = "changelog", required = true) String uuid, ModelMap model) {
        Assert.isEncrypted(decipherer, uuid);
        DefaultVersion version = loadVersion(uuid);
        model.addAttribute(VERSION, version);
        getAllIssues(version, model);
        return "/changelog/index";
    }

    /**
     * Retrieves a version from database.
     *
     * @param uuid a not null encrypted primary key
     * @return any
     */
    protected DefaultVersion loadVersion(String uuid) {
        DefaultVersion version = dao.find(DefaultVersion.class, new UUID(decipherer.decrypt(uuid)));
        return CollectionUtils.isEmpty(collections) ? version : dao.initialize(version, collections.toArray(new String[0]));
    }

    /**
     * Finds all issues for a provided version ordered by alias.
     *
     * @param version any
     * @return a non empty non modifiable collection of issues
     */
    protected List<DefaultIssue> getIssues(DefaultVersion version) {
        Map<String, Object> params = new HashMap<String, Object>(1);
        params.put("targetVersion", version);
        List<DefaultIssue> issues = dao.findByNamedQuery(DefaultIssue.FIND_BY_TARGET_VERSION, 0, 1000, params);
        return CollectionUtils.isEmpty(issues) ? Collections.EMPTY_LIST : Collections.unmodifiableList(issues);
    }

    /**
     * Stores in the model map all issues for the main version and all milestones it contains.
     *
     * @param version any
     * @param model any
     */
    protected void getAllIssues(DefaultVersion version, ModelMap model) {
        if (version != null) {
            addToModel(version.toString(), getIssues(version), model);
            if (CollectionUtils.isNotEmpty(version.getMilestones())) {
                for (DefaultVersion milestone : version.getMilestones()) {
                    addToModel(milestone.toString(), getIssues(milestone), model);
                }
            }
        }
    }

    private void addToModel(String name, List<DefaultIssue> issues, ModelMap model) {
        if (hasText(name) && CollectionUtils.isNotEmpty(issues)) {
            model.addAttribute(name, issues);
        }
    }
}
